Skip to content

use /map: linker flag to avoid running a binary to capture the hash#3133

Open
yoavwizstein wants to merge 7 commits into
aws:mainfrom
yoavwizstein:linux-windows-cross-rebased
Open

use /map: linker flag to avoid running a binary to capture the hash#3133
yoavwizstein wants to merge 7 commits into
aws:mainfrom
yoavwizstein:linux-windows-cross-rebased

Conversation

@yoavwizstein
Copy link
Copy Markdown

@yoavwizstein yoavwizstein commented Mar 29, 2026

Issues:

Resolves #3021

Description of changes:

Replaces the Windows FIPS hash-injection mechanism. Previously, the build linked and ran fips_empty_main.exe to trigger the integrity self-test, captured the expected hash from its stderr, then patched it into crypto.dll. This required a Windows (or Wine) execution environment at build time, blocking cross-compilation.

Now Windows uses the same approach as Linux/macOS: the linker produces a map file (/MAP:), and inject_hash.go -windows parses it alongside the PE to locate the FIPS module boundaries, compute the hash, and patch the DLL directly. No binary execution needed.

Call-outs:

  • fips_empty_main.c and capture_hash/capture_hash.go are deleted entirely.
  • break-hash.go is extended to handle PE via the new fipscommon package (shared with inject_hash.go).
  • The /Fo:/Fo change in fipsmodule/CMakeLists.txt drops the colon separator for compatibility with clang-cl.
  • lib.exe invocation now uses a response file (bcm_objects.rsp) to avoid command-line length limits.
  • lib.exe discovery uses find_program instead of assuming CMAKE_AR or a hardcoded path relative to the linker, improving robustness with clang-cl toolchains.

Testing:

The Windows CI (run_windows_tests.bat) now runs a dedicated fips_build_and_test routine for FIPS dimensions that performs both a positive test (test_fips.exe loads crypto.dll and passes the integrity check + KATs) and a negative test (corrupts the FIPS text region via break-hash.go -map, then confirms test_fips.exe aborts). The cross-compilation path is exercised in the aws-lc-rs.yml workflow which builds with cargo xwin and runs the FIPS sanity test under Wine.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license and the ISC license.

Copy link
Copy Markdown
Contributor

@justsmth justsmth left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for this PR! The approach of using the /MAP: linker flag to avoid running a binary during build aligns with how Linux and macOS already work.

The previous capture_hash approach had a nice side-effect: by running fips_empty_main.exe and observing the integrity check fail (with the placeholder hash), it implicitly proved the integrity check machinery was wired up and functional. With the new static-analysis approach, that implicit verification goes away.

To compensate, I've pushed a commit on top of yours that adds FIPS integrity validation to the Windows CI. It does a couple of things:

  1. Extends break-hash.go with PE support -- this existing tool could only corrupt ELF binaries. It now accepts a -map flag and uses the same map file + debug/pe approach your PR adds to inject_hash.go to locate and corrupt the FIPS module in a Windows DLL.

  2. Added a fips_build_and_test subroutine to run_windows_tests.bat -- after the normal build and test pass, it runs a negative test: corrupts crypto.dll via break-hash.go, runs test_fips.exe, and verifies the process aborts. This proves the integrity check actually runs on DLL load and can detect tampering.

@codecov-commenter
Copy link
Copy Markdown

codecov-commenter commented Apr 8, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.12%. Comparing base (9651480) to head (1dfd266).

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3133   +/-   ##
=======================================
  Coverage   78.12%   78.12%           
=======================================
  Files         689      689           
  Lines      123214   123214           
  Branches    17137    17136    -1     
=======================================
+ Hits        96257    96266    +9     
+ Misses      26047    26039    -8     
+ Partials      910      909    -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@torben-hansen
Copy link
Copy Markdown
Contributor

Haven't looked at the full implementation yet. When inspecting some of the failing CI dimensions, removal of capture_hash.go breaks some assumptions about its presence. I tried re-running them just in case, but I think this PR needs a few more changes to compensate/remove those assumptions.

- aws-lc-rs.yml: rewrite stale Wine-setup comment. The build no longer
  runs fips_empty_main.exe; Wine is only needed for the downstream
  FIPS sanity test step that loads the cross-built DLL.
- run_windows_tests.bat: restore crypto.dll from backup before checking
  the negative-test exit code, so a failure here does not leave a
  corrupted DLL behind for subsequent local invocations.
- inject_hash.go (doWindows): require both BORINGSSL_bcm_rodata_{start,end}
  markers unconditionally. Windows FIPS is always a shared-library build
  and the runtime hashes rodata in that configuration; silently skipping
  rodata here would produce a hash that disagrees with the runtime.
- fipscommon/pe.go: add trailing newline at EOF.
ParseMapFile:
- Anchor parsing to the Publics by Value column header so a same-named
  entry in the Static symbols section can't shadow or collide with a
  public BORINGSSL_bcm_* marker.
- Fail fast with a descriptive error if the map file does not contain
  a Publics by Value section, instead of producing a downstream
  'symbol not found in map file' error.
- Expand the function doc with the expected map-file grammar.

break-hash.go:
- Add a comment on doPE explaining why it does not need the 256-byte
  prefix uniqueness check that doELF performs: the linker map plus the
  PE section table already yield an exact on-disk offset for
  BORINGSSL_bcm_text_start, so the ELF-era workaround is unnecessary.
@justsmth justsmth force-pushed the linux-windows-cross-rebased branch from 45d7a21 to 1dfd266 Compare May 11, 2026 13:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Building fips compliant dll on Windows targets from an Ubuntu machine

4 participants